/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkArenaAllocList_DEFINED
#define SkArenaAllocList_DEFINED

#include "include/private/base/SkAssert.h"
#include "src/base/SkArenaAlloc.h" // IWYU pragma: keep

#include <utility>

/* *
 * A singly linked list of Ts stored in a SkArenaAlloc. The arena rather than the list owns
 * the elements. This supports forward iteration and range based for loops.
 */
template <typename T> class SkArenaAllocList {
private:
    struct Node;

public:
    SkArenaAllocList() = default;

    void reset()
    {
        fHead = fTail = nullptr;
    }

    template <typename... Args> inline T &append(SkArenaAlloc *arena, Args... args);

    class Iter {
    public:
        Iter() = default;
        inline Iter &operator ++ ();
        T &operator*() const
        {
            return fCurr->fT;
        }
        T *operator->() const
        {
            return &fCurr->fT;
        }
        bool operator == (const Iter &that) const
        {
            return fCurr == that.fCurr;
        }
        bool operator != (const Iter &that) const
        {
            return !(*this == that);
        }

    private:
        friend class SkArenaAllocList;
        explicit Iter(Node *node) : fCurr(node) {}
        Node *fCurr = nullptr;
    };

    Iter begin()
    {
        return Iter(fHead);
    }
    Iter end()
    {
        return Iter();
    }
    Iter tail()
    {
        return Iter(fTail);
    }

private:
    struct Node {
        template <typename... Args> Node(Args... args) : fT(std::forward<Args>(args)...) {}
        T fT;
        Node *fNext = nullptr;
    };
    Node *fHead = nullptr;
    Node *fTail = nullptr;
};

template <typename T> template <typename... Args> T &SkArenaAllocList<T>::append(SkArenaAlloc *arena, Args... args)
{
    SkASSERT(!fHead == !fTail);
    auto *n = arena->make<Node>(std::forward<Args>(args)...);
    if (!fTail) {
        fHead = fTail = n;
    } else {
        fTail = fTail->fNext = n;
    }
    return fTail->fT;
}

template <typename T> typename SkArenaAllocList<T>::Iter &SkArenaAllocList<T>::Iter::operator ++ ()
{
    fCurr = fCurr->fNext;
    return *this;
}

#endif
